﻿using System;
using System.Collections.Generic;
using System.Linq;
using FarseerPhysics;
using FarseerPhysics.Common;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Syngine;
using Syngine.Input;
#if !(WINDOWS_PHONE || ANDROID)
using Syngine.Input.Mouse;
#endif
using Syngine.Input.Touch;
using Syngine.Input.Keyboard;
using Syngine.Components.Screens;
using Syngine.Components.Shapes;
using Syngine.Physics;

namespace BenchMark.ShootingGame
{
	public class ShootingGameScreen : Screen
	{
		private readonly List<BlueBox> _boxes = new List<BlueBox>();
		private readonly List<BlueCircle> _circles = new List<BlueCircle>();
		private readonly List<BlueCircle> _deadCircles = new List<BlueCircle>();
		private BlueCircle _shootingCircle;
		private Line _line;

		public ShootingGameScreen(ScreenController screenController) : base(screenController)
		{
		}

		public override void Initialize()
		{
			for (int i = 0; i < 50; i++)
			{
				var circle = new BlueCircle();
				circle.Initialize();
				_circles.Add(circle);
			}

			_line = new Line(Color.GhostWhite);

#if !(WINDOWS_PHONE || ANDROID)
			Input.If(i => i.IsDown(MouseButtons.Right) || i.IsDown(Keys.Space)).Call(AddNewCircle);
			Input.If(i => i.WasReleased(MouseButtons.Left)).Call(ShootNewCircle);
			Input.If(i => i.IsPressed(MouseButtons.Left)).Call(AddNewShootingCircle);
			Input.If(i => i.IsDown(MouseButtons.Left)).Call(c =>
			{
				if (_shootingCircle != null && _line.Visible)
				{
					_line.UpdateDirection(_shootingCircle.CircleBody.Position.ToDisplayUnits(), new Vector2(c.Current.MouseState.X, c.Current.MouseState.Y));
				}
			});
#else
			Input.If(i => i.WasReleased()).Call(ShootNewCircle);
			Input.If(i => i.IsPressed()).Call(AddNewShootingCircle);
			Input.If(i => i.IsDown()).Call(c =>
			{
				if (_shootingCircle != null && _line.Visible)
				{
					_line.UpdateDirection(_shootingCircle.CircleBody.Position.ToDisplayUnits(), c.Current.TouchState.First().Position);
				}
			});
#endif

			base.Initialize();
		}

		private void AddNewShootingCircle(InputCallbackContext obj)
		{
			var loadContext = GameContext.CreateLoadContext();
			_shootingCircle = new BlueCircle(Color.Salmon);

#if !(WINDOWS_PHONE || ANDROID)
			_shootingCircle.LoadContent(loadContext, new Vector2(obj.Current.MouseState.X, obj.Current.MouseState.Y));
#else
			_shootingCircle.LoadContent(loadContext, obj.Current.TouchState.First().Position);
#endif
			_shootingCircle.CircleBody.CollidesWith = Category.None;
			_shootingCircle.CircleBody.Mass = 0.2f;
			_shootingCircle.CircleBody.Awake = false;
			_shootingCircle.CircleBody.UserData = true;

			_line.Visible = true;
			_line.LoadContent(loadContext);
		}

		private void ShootNewCircle(InputCallbackContext context)
		{
			_circles.Add(_shootingCircle);
			_shootingCircle.CircleBody.CollidesWith = Category.All;

#if !(WINDOWS_PHONE || ANDROID)
			var worldTouchPosition = new Vector2(context.Current.MouseState.X, context.Current.MouseState.Y).ToSimUnits();
#else
			var worldTouchPosition = context.Current.TouchState.First().Position.ToSimUnits();
#endif

			var direction = Vector2.Normalize(_shootingCircle.Position - worldTouchPosition);

			if (!direction.IsValid())
			{
				direction = Vector2.One;
			}

			var distance = Vector2.Distance(_shootingCircle.Position, worldTouchPosition);

			if (Math.Abs(distance - 0.0f) <= 0.0f)
			{
				distance = 0.01f;
			}

			var force = ((_shootingCircle.ForceConstant * direction) * distance);

			_shootingCircle.ApplyForce(force);

			_shootingCircle = null;
			_line.Visible = false;
		}

#if !(WINDOWS_PHONE || ANDROID)
		private void AddNewCircle(InputCallbackContext context)
		{
			if (_boxes.Count < 250)
			{
				var ball = new BlueCircle(Rand.Color);
				ball.Initialize();
				ball.LoadContent(GameContext.CreateLoadContext(), new Vector2(context.Current.MouseState.X, context.Current.MouseState.Y));
				ball.CircleBody.UserData = false;
				_circles.Add(ball);
			}
		}
#endif

		public override void LoadContent(LoadContext context)
		{
			var game = GameContext.Game;
			var bounds = BodyFactory.CreateLoopShape(WorldContext.World, GetWorldBounds(game.GraphicsDevice), 1f);
			GameContext.Camera.Focus = new Vector2(game.GraphicsDevice.Viewport.Width / 2.0f, game.GraphicsDevice.Viewport.Height / 2.0f);
			bounds.CollidesWith = Category.All;
			_boxes.ForEach(b => b.LoadContent(context));
			_circles.ForEach(c =>
			{
				c.LoadContent(context);
				c.CircleBody.UserData = false;
			});
		}

		public override void UnloadContent()
		{
			_boxes.ForEach(b => b.UnloadContent());
			_circles.ForEach(c => c.UnloadContent());
		}

		protected override void UpdateCore(UpdateContext context)
		{
			_boxes.ForEach(b => b.Update(context));

			_circles.ForEach(c =>
			{
				if (!c.IsDead)
				{
					c.Update(context);
				}
				else
				{
					_deadCircles.Add(c);
				}
			});

			_deadCircles.ForEach(c =>
			{
				_circles.Remove(c);
				WorldContext.World.RemoveBody(c.CircleBody);
			});

			_deadCircles.Clear();

			_line.Update(context);

			base.UpdateCore(context);
		}

		protected override void DrawCore(DrawContext context)
		{
			_circles.ForEach(c => c.Draw(context));

			if (_shootingCircle != null)
			{
				_shootingCircle.Draw(context);
			}

			_line.Draw(context);
		}

		private static Color GetRandomColor()
		{
			var random = Rand.Random;
			return new Color(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255), random.Next(0, 255));
		}

		private static Vertices GetWorldBounds(GraphicsDevice graphics)
		{
			var width = ConvertUnits.ToSimUnits(graphics.Viewport.Width);
			var height = ConvertUnits.ToSimUnits(graphics.Viewport.Height);

			return new Vertices(new[]
			{
				new Vector2(0, 0),
				new Vector2(width, 0),
				new Vector2(width, height),
				new Vector2(0, height)
			});
		}
	}
}